package com.eidos.blogs.openapi.thread.seckill;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.eidos.blogs.openapi.dto.secKill.RedEnvelopeDto;
import com.eidos.blogs.openapi.entity.seckill.RedeInfo;
import com.eidos.blogs.openapi.entity.seckill.RedeReceiveRecord;
import com.eidos.blogs.openapi.entity.seckill.RedeReceiveSyncRecords;
import com.eidos.blogs.openapi.rest.HttpCodeEnum;
import com.eidos.blogs.openapi.rest.Resp;
import com.eidos.blogs.openapi.service.seckill.IRedeInfoService;
import com.eidos.blogs.openapi.service.seckill.IRedeReceiveRecordService;
import com.eidos.blogs.openapi.service.seckill.IRedeReceiveSyncRecordsService;
import com.eidos.blogs.openapi.utils.RandomNumberUtils;
import com.eidos.blogs.openapi.vo.secKill.redenvelope.RedEnvelopeVo;
import lombok.extern.slf4j.Slf4j;
import org.redisson.Redisson;
import org.redisson.api.RLock;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.util.CollectionUtils;

import java.time.LocalDateTime;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.stream.Collectors;

/**
 * @author ： Eidos
 * @date ：Created in 2021/1/27
 * @description：
 * @modified By：
 * @version: 1.0
 */
@Slf4j
public class RedEnVelopeFutureTask<T> implements Callable<Resp<Object>> {

    @Autowired
    private Redisson redisson;
    @Autowired
    private RedisTemplate redisTemplate;
    @Autowired
    private IRedeInfoService redeInfoService;
    @Autowired
    private IRedeReceiveRecordService redeReceiveRecordService;
    @Autowired
    private IRedeReceiveSyncRecordsService redeReceiveSyncRecordsService;

    private  RedEnvelopeDto dto;

    public RedEnVelopeFutureTask(RedEnvelopeDto dto){
        this.dto=dto;
    }


    @Override
    public Resp<Object> call() throws Exception {
        Long money = 0L;
        String lockKey = "redEnvelope_lock_" + dto.getRedEnvelopeId();
        String numberKey = "redEnvelope_number_" + dto.getRedEnvelopeId();
        String amountKey = "redEnvelope_amount_" + dto.getRedEnvelopeId();
        RLock lock = redisson.getLock(lockKey);
        // 添加补偿机制
        Long  oldNumber=-1L;
        Long  oldMoney=-1L;
        try {
            /**
             * 获取锁 保证红包数量和计算红包金额的原子性操作
             */
            lock.lock();
            if (redisTemplate.opsForValue().get(numberKey) == null || redisTemplate.opsForValue().get(amountKey) == null) {
                return Resp.failed("红包已经抢完了");
            }
            Long restNumber = Long.valueOf(redisTemplate.opsForValue().get(numberKey).toString());
            Long restMoney = Long.valueOf(redisTemplate.opsForValue().get(amountKey).toString());
            oldNumber=restNumber;
            oldMoney=restMoney;
            if(restNumber-1<0){
                return Resp.failed("红包已经抢完了");
            }
            log.info("异步入redis-用户");
            String redisKey="redEnvelope_"+dto.getRedEnvelopeId()+"_"+dto.getUserId();
            Boolean flag = redisTemplate.opsForValue().setIfAbsent(redisKey, dto.getUserId());
            if (!flag) {
                return Resp.failed("该用户"+dto.getUserId()+"已经抢过了，不能重复抢");
            }
            LocalDateTime nowTime = LocalDateTime.now();
            log.info("目前红包个数为：" + restNumber);
            log.info("目前红包总金额为：" + restMoney);
            restNumber = redisTemplate.opsForValue().decrement(numberKey, 1);
            /**
             * 如果是最后一人
             */
            if (restNumber == 0) {
                money = restMoney;
            } else {
                if (dto.getReceiveAmount() != null && dto.getReceiveAmount().compareTo(0L) > 0) {
                    money = dto.getReceiveAmount();
                } else {
                    money = RandomNumberUtils.getRandomMoney(restNumber.intValue(), restMoney, dto.getOpenHit(), dto.getHitNums());
                }
            }
            Long decrementMoney = redisTemplate.opsForValue().decrement(amountKey, money);

            log.info("异步入库-用户" + dto.getUserId());
            log.info("抢到红包-金额（分）为：" + money);
            log.info("异步入库-插入领取红包记录表");
            RedeReceiveRecord redeReceiveRecord = new RedeReceiveRecord();
            redeReceiveRecord.setRedEnvelopeId(dto.getRedEnvelopeId());
            redeReceiveRecord.setUserId(dto.getUserId());
            redeReceiveRecord.setAmount(money);
            redeReceiveRecord.setCreateTime(nowTime);

            log.info("异步入库-插入同步红包信息");
            log.info("异步入库-剩余红包金额：" + decrementMoney);
            log.info("异步入库-剩余红包个数为：" + restNumber.intValue());
            QueryWrapper<RedeReceiveSyncRecords> wrapper2 = new QueryWrapper<>();
            wrapper2.eq("red_envelope_id", dto.getRedEnvelopeId());
            List<RedeReceiveSyncRecords> syncRecords = redeReceiveSyncRecordsService.getBaseMapper().selectList(wrapper2);
            if (syncRecords != null && syncRecords.size() > 0) {
                RedeReceiveSyncRecords redeReceiveSyncRecords = syncRecords.get(0);
                redeReceiveSyncRecords.setRestNum(restNumber.intValue());
                redeReceiveSyncRecords.setRestAmount(decrementMoney);
                redeReceiveSyncRecords.setCreateTime(nowTime);

                redeReceiveRecordService.add(redeReceiveRecord);
                redeReceiveSyncRecordsService.modify(redeReceiveSyncRecords);
            }else{
                return Resp.failed(HttpCodeEnum.DATA_NULL);
            }
            // 如果为0，表示红包已经抢完
            if (restNumber == 0) {
                // 删除redis数据
                redisTemplate.delete(numberKey);
                redisTemplate.delete(amountKey);
                // 修改红包信息表状态为禁用0
                RedeInfo po = new RedeInfo();
                po.setStatus(0);
                QueryWrapper<RedeInfo> wrapper = new QueryWrapper<>();
                wrapper.eq("red_envelope_id", dto.getRedEnvelopeId());
                redeInfoService.update(po,wrapper);
                // 计算手气最佳
                QueryWrapper<RedeReceiveRecord> wrapper3 = new QueryWrapper<>();
                wrapper3.eq("red_envelope_id", dto.getRedEnvelopeId());
                List<RedeReceiveRecord> redeReceiveRecords = redeReceiveRecordService.getBaseMapper().selectList(wrapper3);
                if(!CollectionUtils.isEmpty(redeReceiveRecords)){
                    RedeReceiveRecord redeReceiveRecord1 = redeReceiveRecords.stream().sorted((o1, o2) -> {
                        return o2.getAmount().compareTo(o1.getAmount());
                    }).collect(Collectors.toList()).get(0);
                    redeReceiveRecord1.setBest(1);
                    redeReceiveRecordService.modify(redeReceiveRecord1);
                }

            }
            // TODO 异步入账
            RedEnvelopeVo redEnvelopeVo = new RedEnvelopeVo();
            redEnvelopeVo.setRedEnvelopeId(dto.getRedEnvelopeId());
            redEnvelopeVo.setUserId(dto.getUserId());
            redEnvelopeVo.setRestNum(restNumber.intValue());
            redEnvelopeVo.setMoney(money);
            redEnvelopeVo.setCreateTime(nowTime);
            return Resp.success(redEnvelopeVo);
        } catch (Exception e) {
            e.printStackTrace();
            log.info("产生异常，异常信息" + e.getMessage());
            // 添加补偿机制
            if(oldNumber.compareTo(0L)>=0 && oldMoney.compareTo(0L)>=0){
                Long restNumber = Long.valueOf(redisTemplate.opsForValue().get(numberKey).toString());
                if(restNumber==0){
                    redisTemplate.opsForValue().set(numberKey,oldNumber);
                    redisTemplate.opsForValue().set(amountKey,oldMoney);
                    // 修改红包信息表状态为禁用0
                    RedeInfo po = new RedeInfo();
                    po.setStatus(1);
                    QueryWrapper<RedeInfo> wrapper = new QueryWrapper<>();
                    wrapper.eq("red_envelope_id", dto.getRedEnvelopeId());
                    redeInfoService.update(po,wrapper);

                }
                redisTemplate.opsForValue().set(numberKey,oldNumber);
                redisTemplate.opsForValue().set(amountKey,oldMoney);
                RedeReceiveRecord redeReceiveRecord = new RedeReceiveRecord();
                redeReceiveRecord.setRedEnvelopeId(dto.getRedEnvelopeId());
                redeReceiveRecord.setUserId(dto.getUserId());
                redeReceiveRecord.setAmount(oldMoney);
                QueryWrapper<RedeReceiveSyncRecords> wrapper2 = new QueryWrapper<>();
                wrapper2.eq("red_envelope_id", dto.getRedEnvelopeId());
                List<RedeReceiveSyncRecords> syncRecords = redeReceiveSyncRecordsService.getBaseMapper().selectList(wrapper2);
                if (syncRecords != null && syncRecords.size() > 0) {
                    RedeReceiveSyncRecords redeReceiveSyncRecords = syncRecords.get(0);
                    redeReceiveSyncRecords.setRestNum(oldNumber.intValue());
                    redeReceiveSyncRecords.setRestAmount(oldMoney);
                    redeReceiveRecordService.add(redeReceiveRecord);
                    redeReceiveSyncRecordsService.modify(redeReceiveSyncRecords);
                }else{
                    return Resp.failed(HttpCodeEnum.DATA_NULL);
                }
            }
            return Resp.failed(HttpCodeEnum.SYSTEM_ERROR);
        } finally {
            lock.unlock();
        }

    }
}
